﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.UI;
using YArchitech.Controls;
using YArchitech.LIB;
using YArchitech.Revit;
using YJKGeometryFuncs;
using YJKRevitView;

namespace YJKFurniture.LayoutFurnitures
{
	[Transaction(TransactionMode.Manual)]
	internal class CmdArchFurnitureLayout : IExternalCommand
	{
		public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			this.m_uiDoc = commandData.Application.ActiveUIDocument;
			this.m_doc = this.m_uiDoc.Document;
			bool flag = false;
			if (SwitchView.SetUsableView(CmdViewType.FloorPlan, commandData) != Autodesk.Revit.UI.Result.Succeeded)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			try
			{
				while (!flag)
				{
					this.m_strFamilySymbolName = null;
					this.m_famSym = null;
					LayFurnitureForm layFurnitureForm = new LayFurnitureForm(commandData);
					if (layFurnitureForm.ShowDialog() == DialogResult.OK)
					{
						this.m_strFamilySymbolName = layFurnitureForm.m_strFamilySymbolName;
						this.m_famSym = layFurnitureForm.m_famSym;
						this.m_LayoutStyle = layFurnitureForm.m_LayoutStyle;
						this.m_nAlongWallNum = layFurnitureForm.m_nAlongWallNum;
						this.m_dAlongWallSpace = (double)layFurnitureForm.m_nAlongWallSpace / CmdArchFurnitureLayout.m_dFeetToMM;
						this.m_dAlongWallToFace = (double)layFurnitureForm.m_nAlongWallToFace / CmdArchFurnitureLayout.m_dFeetToMM;
						this.m_dAlongWallToSide = (double)layFurnitureForm.m_nAlongWallToSide / CmdArchFurnitureLayout.m_dFeetToMM;
						this.m_dAlongFurnitureOffset = (double)layFurnitureForm.m_nAlongFurnitureOffset / CmdArchFurnitureLayout.m_dFeetToMM;
						this.m_dAlongFurnitureSpace = (double)layFurnitureForm.m_nAlongFurnitureSpace / CmdArchFurnitureLayout.m_dFeetToMM;
						this.m_nAlongFurnitureNum = layFurnitureForm.m_nAlongFurnitureNum;
						layFurnitureForm.Close();
					}
					if (this.m_strFamilySymbolName == null && this.m_famSym == null)
					{
						return Autodesk.Revit.UI.Result.Succeeded;
					}
					flag = this.CreateFurniture();
				}
			}
			catch (Exception)
			{
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private bool CreateFurniture()
		{
			using (Transaction transaction = new Transaction(this.m_doc, "布置家具"))
			{
				try
				{
					transaction.Start();
					if (this.m_famSym == null)
					{
						Family family = null;
						FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
						filteredElementCollector.OfClass(typeof(Family));
						List<Element> list = filteredElementCollector.ToElements().ToList<Element>();
						if (list != null && list.Count != 0)
						{
							foreach (Element element in list)
							{
								Family family2 = element as Family;
								if (family2.Name == this.m_strFamilySymbolName)
								{
									family = family2;
									break;
								}
							}
						}
						if (family == null)
						{
							string text = Path.Combine(Product.FamilyLocation, this.m_doc.Application.VersionNumber, "EquipmentFurniture", this.m_strFamilySymbolName + ".rfa");
							if (!File.Exists(text))
							{
								YJKMessageBox.Show("未找到需要加载的族文件", MessageBoxButtons.OK);
								transaction.RollBack();
								return true;
							}
							if (!this.m_doc.LoadFamily(text, out family))
							{
								YJKMessageBox.Show("族文件加载失败", MessageBoxButtons.OK);
								transaction.RollBack();
								return true;
							}
						}
						IEnumerable<ElementId> familySymbolIds = family.GetFamilySymbolIds();
						ElementId elementId = null;
						IEnumerator<ElementId> enumerator2 = familySymbolIds.GetEnumerator();
						{
							if (enumerator2.MoveNext())
							{
								elementId = enumerator2.Current;
							}
						}
						this.m_famSym = (this.m_doc.GetElement(elementId) as FamilySymbol);
					}
					this.m_famSym.Activate();
					transaction.Commit();
				}
				catch
				{
					YJKMessageBox.Show("族文件加载出错", MessageBoxButtons.OK);
					transaction.RollBack();
					return true;
				}
			}
			if (this.m_LayoutStyle == LayoutStyle.LayoutFree)
			{
				this.LayoutFurnitureFree();
			}
			else if (this.m_LayoutStyle == LayoutStyle.LayoutAlongWall)
			{
				this.LayoutFurnitureAlongWall();
			}
			else if (this.m_LayoutStyle == LayoutStyle.LayoutAlongFurniture)
			{
				this.LayoutFurnitureAlongFurniture();
			}
			return false;
		}

		private void LayoutFurnitureFree()
		{
			try
			{
				this.m_uiDoc.PromptForFamilyInstancePlacement(this.m_famSym);
			}
			catch (Autodesk.Revit.Exceptions.OperationCanceledException)
			{
			}
			catch (Exception)
			{
			}
		}

		private BoundingBoxXYZ GetInstanceBoundingBox(FamilySymbol famSym, ref XYZ leftDown, ref XYZ rightUp, ref XYZ leftUp, ref XYZ rightDown, ref double dLength, ref double dWidth)
		{
			Level genLevel = this.m_doc.ActiveView.GenLevel;
			FamilyInstance familyInstance = null;
			using (Transaction transaction = new Transaction(this.m_doc, "GetInstaceBoundingBox"))
			{
				try
				{
					transaction.Start();
					familyInstance = this.m_doc.Create.NewFamilyInstance(new XYZ(), famSym, genLevel, (Autodesk.Revit.DB.Structure.StructuralType)0);
					transaction.Commit();
				}
				catch (Exception)
				{
					transaction.RollBack();
				}
			}
			BoundingBoxXYZ boundingBoxXYZ = familyInstance.get_BoundingBox(this.m_doc.ActiveView);
			leftDown = boundingBoxXYZ.Min;
			rightUp = boundingBoxXYZ.Max;
			leftUp = new XYZ(leftDown.X, rightUp.Y, leftDown.Z);
			rightDown = new XYZ(rightUp.X, leftDown.Y, rightUp.Z);
			dLength = rightUp.X - leftDown.X;
			dWidth = rightUp.Y - leftDown.Y;
			using (Transaction transaction2 = new Transaction(this.m_doc, "DeleteInstace"))
			{
				try
				{
					transaction2.Start();
					this.m_doc.Delete(familyInstance.Id);
					transaction2.Commit();
				}
				catch (Exception)
				{
					transaction2.RollBack();
				}
			}
			return boundingBoxXYZ;
		}

		private void LayoutFurnitureAlongWall()
		{
			this.m_BBox = this.GetInstanceBoundingBox(this.m_famSym, ref this.m_LeftDown, ref this.m_RightUp, ref this.m_LeftUp, ref this.m_RightDown, ref this.m_dLength, ref this.m_dWidth);
			XYZ xyz = new XYZ();
			XYZ xyz2 = new XYZ();
			double num = 0.0;
			bool flag = false;
			Line line = null;
			Transform transform = null;
			XYZ xyz3 = new XYZ();
			XYZ xyz4 = new XYZ();
			Curve curve = this.SelectWall(ref xyz, ref xyz2, ref num, ref xyz3, ref xyz4, ref flag, ref transform);
			if (!flag)
			{
				line = (curve as Line);
			}
			if (line == null)
			{
				YJKMessageBox.Show("没有选到墙边线，请将鼠标放到墙边线上，亮显后再确定！", MessageBoxButtons.OK);
			}
			if (line != null)
			{
				List<FamilyInstance> list = new List<FamilyInstance>();
				ICollection<ElementId> collection = new List<ElementId>();
				using (Transaction transaction = new Transaction(this.m_doc, "CreateFurnitureAlongWall"))
				{
					try
					{
						transaction.Start();
						double num2 = this.m_dWidth + this.m_dAlongWallSpace;
						for (int i = 0; i < this.m_nAlongWallNum; i++)
						{
							FamilyInstance familyInstance = this.m_doc.Create.NewFamilyInstance(new XYZ(0.0, (double)i * num2, 0.0), this.m_famSym, this.m_doc.ActiveView.GenLevel, (Autodesk.Revit.DB.Structure.StructuralType)0);
							list.Add(familyInstance);
							collection.Add(familyInstance.Id);
						}
						double num3 = XYZ.BasisY.AngleOnPlaneTo(xyz3, XYZ.BasisZ);
						ElementTransformUtils.RotateElements(this.m_doc, collection, Line.CreateBound(XYZ.Zero, XYZ.BasisZ), num3);
						Transform trans = Transform.CreateRotation(XYZ.BasisZ, num3);
						XYZ referencePoint = this.GetReferencePoint(trans, xyz4);
						XYZ xyz5 = xyz + xyz4 * this.m_dAlongWallToFace + xyz3 * this.m_dAlongWallToSide - referencePoint;
						ElementTransformUtils.MoveElements(this.m_doc, collection, xyz5);
						transaction.Commit();
					}
					catch (Exception)
					{
						transaction.RollBack();
					}
				}
			}
		}

		private XYZ GetReferencePoint(Transform trans, XYZ normalVector)
		{
			XYZ xyz = trans.OfVector(-XYZ.BasisX);
			trans.OfVector(XYZ.BasisX);
			if (normalVector.IsAlmostEqualTo(xyz))
			{
				return trans.OfPoint(this.m_RightDown);
			}
			return trans.OfPoint(this.m_LeftDown);
		}

		private Curve SelectWall(ref XYZ startPoint, ref XYZ endPoint, ref double z, ref XYZ lineVector, ref XYZ normalVector, ref bool isArcWall, ref Transform trans)
		{
			Reference reference = null;
			Line line = null;
			try
			{
				SelectedElement selectedElement = this.m_uiDoc.PickElementsByFace<Wall>("请选择要布置的参考墙面", ref reference, ref trans, 0.05);
				if (selectedElement == null)
				{
					return line;
				}
				Element element = selectedElement.Element;
				Wall wall = element as Wall;
				z = this.m_uiDoc.ActiveView.GenLevel.Elevation;
				new Options().ComputeReferences = true;
				PlanarFace planarFace = null;
				try
				{
					planarFace = ((element.GetGeometryObjectFromReference(reference) as Face) as PlanarFace);
				}
				catch (Exception)
				{
					planarFace = this.GetNearestFaceFromElement(element, reference.GlobalPoint, trans);
				}
				Line line2 = null;
				double num = double.MaxValue;
				bool flag = false;
				foreach (object obj in planarFace.EdgeLoops.get_Item(0))
				{
					Edge edge = (Edge)obj;
					if (edge.Tessellate().Count == 2)
					{
						if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) < 0.01)
						{
							flag = true;
							Line line3 = Line.CreateBound(edge.Tessellate()[0], edge.Tessellate()[1]);
							double num2 = line3.Distance(reference.GlobalPoint);
							if (trans != null)
							{
								num2 = line3.Distance(reference.GlobalPoint - trans.Origin);
							}
							if (num2 < num)
							{
								num = num2;
								this.GetIntercept(edge.Tessellate()[0], edge.Tessellate()[1]);
								line2 = line3;
							}
						}
					}
					else
					{
						isArcWall = true;
					}
				}
				if (!flag)
				{
					return null;
				}
				line = line2;
				this.GetVectorOfMove(wall, line, ref lineVector, ref normalVector, ref startPoint, ref endPoint, reference, trans);
			}
			catch (Exception)
			{
				return line;
			}
			return line;
		}

		private PlanarFace GetNearestFaceFromElement(Element e, XYZ globalPoint, Transform trans)
		{
			PlanarFace result = null;
			try
			{
				foreach (GeometryObject geometryObject in (e as Wall).get_Geometry(new Options
				{
					ComputeReferences = true
				}))
				{
					if (geometryObject is Solid)
					{
						Solid solid = geometryObject as Solid;
						if (solid != null && solid.Edges.Size > 0)
						{
							double num = double.MaxValue;
							foreach (object obj in solid.Faces)
							{
								Face face = (Face)obj;
								if (this.isVerticalFace(face))
								{
									int num2 = 0;
									double num3 = double.MaxValue;
									foreach (object obj2 in face.EdgeLoops.get_Item(0))
									{
										Edge edge = (Edge)obj2;
										num2++;
										if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) < 0.1)
										{
											double num4 = Line.CreateBound(edge.Tessellate()[0], edge.Tessellate()[1]).Distance(globalPoint - trans.Origin);
											if (num4 < num3)
											{
												num3 = num4;
											}
										}
									}
									if (num3 < num)
									{
										num = num3;
										result = (face as PlanarFace);
									}
								}
							}
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return result;
		}

		private double GetIntercept(XYZ point1, XYZ point2)
		{
			if (point1.X - point2.X == 0.0)
			{
				return double.MaxValue;
			}
			double num = (point1.Y - point2.Y) / (point1.X - point2.X);
			return point1.Y - num * point1.X;
		}

		private bool isVerticalFace(Face f)
		{
			foreach (object obj in f.EdgeLoops.get_Item(0))
			{
				Edge edge = (Edge)obj;
				if (Math.Abs(edge.Tessellate()[0].Z - edge.Tessellate()[1].Z) > 0.1)
				{
					return true;
				}
			}
			return false;
		}

		private void GetVectorOfMove(Wall wall, Line line, ref XYZ lineVector, ref XYZ normalVector, ref XYZ lineStart, ref XYZ lineEnd, Reference reference, Transform trans)
		{
			Curve curve = (wall.Location as LocationCurve).Curve;
			curve.GetEndPoint(0);
			curve.GetEndPoint(1);
			if (line != null)
			{
				YJKLine hyline = new YJKLine(line);
				lineStart = hyline.GetNearlyPoint(reference.GlobalPoint);
				if (trans != null)
				{
					lineStart = hyline.GetNearlyPoint(reference.GlobalPoint - trans.Origin);
				}
				if (lineStart.DistanceTo(line.Tessellate()[0]) > lineStart.DistanceTo(line.Tessellate()[1]))
				{
					lineEnd = line.Tessellate()[0];
				}
				else
				{
					lineEnd = line.Tessellate()[1];
				}
			}
			lineVector = (lineEnd - lineStart).Normalize();
			XYZ xyz = Geometry.CalculateFootPoint(curve as Line, lineStart);
			normalVector = (lineStart - xyz).Normalize();
		}

		private void LayoutFurnitureAlongFurniture()
		{
			try
			{
				XYZ xyz = new XYZ();
				Element element;
				for (;;)
				{
					SelectedElement selectedElement = this.m_uiDoc.PickElementsByPoint("请选择一个家具", 0.05);
					element = selectedElement.Element;
					xyz = selectedElement.SelectedPoint;
					if (!(element.Category.Name != "家具"))
					{
						break;
					}
					YJKMessageBox.Show("请选择一个家具", MessageBoxButtons.OK);
				}
				if (element != null)
				{
					FamilyInstance instance = element as FamilyInstance;
					XYZ xyz2 = (this.m_uiDoc.Selection.PickPoint() - xyz).Normalize();
					bool flag = this.CalculateLayVector(ref xyz2);
					XYZ firstLocation = this.GetFirstLocation(instance, xyz2, flag);
					double num;
					if (flag)
					{
						num = this.m_dLength + this.m_dAlongFurnitureSpace;
					}
					else
					{
						num = this.m_dWidth + this.m_dAlongFurnitureSpace;
					}
					List<XYZ> list = new List<XYZ>();
					list.Add(firstLocation);
					for (int i = 1; i < this.m_nAlongFurnitureNum; i++)
					{
						XYZ item = firstLocation + xyz2 * (double)i * num;
						list.Add(item);
					}
					using (Transaction transaction = new Transaction(this.m_doc, "LayoutFurnitureAlongFurniture"))
					{
						try
						{
							transaction.Start();
							for (int j = 0; j < list.Count; j++)
							{
								this.m_doc.Create.NewFamilyInstance(list[j], this.m_famSym, this.m_doc.ActiveView.GenLevel, (Autodesk.Revit.DB.Structure.StructuralType)0);
							}
							transaction.Commit();
						}
						catch (Exception)
						{
							transaction.RollBack();
						}
					}
				}
			}
			catch (Exception)
			{
			}
		}

		private bool CalculateLayVector(ref XYZ layNormalVector)
		{
			double num = Math.PI;
			double num2 = XYZ.BasisX.AngleOnPlaneTo(layNormalVector, XYZ.BasisZ);
			bool flag = true;
			bool flag2 = true;
			if (num2 > num)
			{
				flag2 = false;
				num2 = 2.0 * num - num2;
			}
			if (num2 > num / 2.0)
			{
				flag = false;
				num2 = num - num2;
			}
			bool result = true;
			double num3 = 0.0;
			double num4 = 0.0;
			if (num2 <= this.m_dAngleTolerance)
			{
				num3 = 1.0;
				num4 = 0.0;
				result = true;
			}
			else if (num2 > this.m_dAngleTolerance && num2 <= num / 4.0)
			{
				num3 = 1.0;
				num4 = num3 * Math.Tan(num2);
				result = true;
			}
			else if (num2 > num / 4.0 && num2 < num / 2.0 - this.m_dAngleTolerance)
			{
				num4 = 1.0;
				num3 = num4 / Math.Tan(num2);
				result = false;
			}
			else if (num2 >= num / 2.0 - this.m_dAngleTolerance)
			{
				num3 = 0.0;
				num4 = 1.0;
				result = false;
			}
			if (!flag)
			{
				num3 = -num3;
			}
			if (!flag2)
			{
				num4 = -num4;
			}
			layNormalVector = new XYZ(num3, num4, 0.0);
			return result;
		}

		private XYZ GetFirstLocation(FamilyInstance instance, XYZ layVector, bool bBaseX)
		{
			this.m_BBox = this.GetInstanceBoundingBox(this.m_famSym, ref this.m_LeftDown, ref this.m_RightUp, ref this.m_LeftUp, ref this.m_RightDown, ref this.m_dLength, ref this.m_dWidth);
			BoundingBoxXYZ boundingBoxXYZ = instance.get_BoundingBox(this.m_doc.ActiveView);
			XYZ point = (instance.Location as LocationPoint).Point;
			XYZ xyz = boundingBoxXYZ.Max - point;
			XYZ xyz2 = boundingBoxXYZ.Min - point;
			double num = this.m_dAlongFurnitureOffset;
			if (bBaseX)
			{
				if (layVector.X > 0.0)
				{
					num = this.m_dAlongFurnitureOffset + Math.Abs(xyz.X) + Math.Abs(this.m_LeftUp.X);
				}
				else
				{
					num = this.m_dAlongFurnitureOffset + Math.Abs(xyz2.X) + Math.Abs(this.m_RightUp.X);
				}
			}
			else if (layVector.Y > 0.0)
			{
				num = this.m_dAlongFurnitureOffset + Math.Abs(xyz.Y) + Math.Abs(this.m_RightDown.Y);
			}
			else
			{
				num = this.m_dAlongFurnitureOffset + Math.Abs(xyz2.Y) + Math.Abs(this.m_RightUp.Y);
			}
			new XYZ();
			return point + num * layVector;
		}

		public static double m_dFeetToMM = 304.8;

		public static double m_dMToMM = 1000.0;

		private UIDocument m_uiDoc;

		private Document m_doc;

		private string m_strFamilySymbolName;

		private FamilySymbol m_famSym;

		private LayoutStyle m_LayoutStyle;

		private double m_dRelativeHeight;

		private int m_nAlongWallNum;

		private double m_dAlongWallSpace;

		private double m_dAlongWallToFace;

		private double m_dAlongWallToSide;

		private double m_dAlongFurnitureOffset;

		private double m_dAlongFurnitureSpace;

		private int m_nAlongFurnitureNum;

		private BoundingBoxXYZ m_BBox = new BoundingBoxXYZ();

		private XYZ m_LeftUp = new XYZ();

		private XYZ m_LeftDown = new XYZ();

		private XYZ m_RightDown = new XYZ();

		private XYZ m_RightUp = new XYZ();

		private double m_dLength;

		private double m_dWidth;

		private double m_dAngleTolerance = 0.087266462599716474;
	}
}
